{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-10-14T10:06:28.862071Z",
     "start_time": "2025-10-14T10:06:12.985475Z"
    }
   },
   "source": [
    "import os\n",
    "import torch\n",
    "from dotenv import load_dotenv\n",
    "from langchain_chroma import Chroma\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_community.llms.tongyi import Tongyi\n",
    "from langchain_huggingface import HuggingFaceEmbeddings\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables.passthrough import RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI\n",
    "from ragas.llms import llm_factory\n",
    "\n",
    "# 加载嵌入模型\n",
    "embedding_model = HuggingFaceEmbeddings(\n",
    "    model_name=\"./bge-base-zh-v1.5\",\n",
    "    model_kwargs={\"device\": \"cuda\" if torch.cuda.is_available() else \"cpu\"},\n",
    "    encode_kwargs={\"normalize_embeddings\": True},\n",
    ")\n",
    "\n",
    "# 初始化 Chroma 客户端\n",
    "vectorstore = Chroma(\n",
    "    persist_directory=\"vectorstore\",\n",
    "    embedding_function=embedding_model,\n",
    ")\n",
    "\n",
    "# 创建检索器\n",
    "retriever = vectorstore.as_retriever(\n",
    "    search_type=\"similarity\",  # 检索方式，similarity 或 mmr\n",
    "    search_kwargs={\"k\": 3},\n",
    ")\n"
   ],
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/miniconda3/envs/rag-demo/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n",
      "/opt/miniconda3/envs/rag-demo/lib/python3.10/site-packages/torchvision/io/image.py:13: UserWarning: Failed to load image Python extension: 'dlopen(/opt/miniconda3/envs/rag-demo/lib/python3.10/site-packages/torchvision/image.so, 0x0006): Symbol not found: __ZN3c1017RegisterOperatorsD1Ev\n",
      "  Referenced from: <9B280146-BBD7-3F77-9873-F9740F2A5329> /opt/miniconda3/envs/rag-demo/lib/python3.10/site-packages/torchvision/image.so\n",
      "  Expected in:     <078DF3B7-D63C-3CEF-94C1-C063C5E5A468> /opt/miniconda3/envs/rag-demo/lib/libtorch_cpu.dylib'If you don't plan on using image functionality from `torchvision.io`, you can ignore this warning. Otherwise, there might be something wrong with your environment. Did you have `libjpeg` or `libpng` installed before building `torchvision` from source?\n",
      "  warn(\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-14T10:20:11.907601Z",
     "start_time": "2025-10-14T10:20:08.849410Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 检索与生成链条\n",
    "\n",
    "# 1、获取大模型\n",
    "load_dotenv()\n",
    "TONGYI_API_KEY = os.getenv(\"TONGYI_API_KEY\")\n",
    "# 1、在线调用百炼平台的模型\n",
    "llm = Tongyi(model=\"qwen-turbo\", api_key=TONGYI_API_KEY)\n",
    "\n",
    "# 2、通用的调用方式：在线调用其他平台、本地部署的模型（vllm、ollama）\n",
    "# llm=ChatOpenAI(\n",
    "#     model=\"qwen-turbo\",# 如果vllm部署，必须与vllm启动时指定的model_server_name一致\n",
    "#     api_key=TONGYI_API_KEY, # 如果vllm部署，内容随便填，但是这个参数不能为空\n",
    "#     base_url=\"https://dashscope.aliyuncs.com/api/v1\",#如果vllm部署，写vllm的地址，http://xxxxx:8000/v1\n",
    "#     streaming=True,  #是否流式输出\n",
    "#     extra_body={\n",
    "#         \"chat_template_kwargs\": {\"enable_thinking\": False}  #可以指定关闭思考模式\n",
    "#     },\n",
    "# )\n",
    "\n",
    "\n",
    "# 将检索到的文档中的 page_content 取出组合到一起\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join([doc.page_content for doc in docs])\n",
    "\n",
    "\n",
    "# 2、Prompt 模板\n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"context\", \"query\"],\n",
    "    template=\"\"\"\n",
    "你是一个专业的中文问答助手，擅长基于提供的资料回答用户问题。\n",
    "请仅根据以下背景资料回答问题，如无法找到答案，请直接回答“我不知道”。\n",
    "\n",
    "背景资料：{context}\n",
    "\n",
    "问题：{query}\n",
    "\n",
    "回答：\"\"\",\n",
    ")\n",
    "\n",
    "# 3、构建RAG 链条\n",
    "# 在LCEL链条中，需要调用自己的函数，写成lambda表达式，就会自动转成Runable接口类型，不需要自己转\n",
    "# 在LCEL链条中，需要调用自己的函数，中间有管道符，也会自动转成Runable接口类型，不需要自己转\n",
    "rag_chains = (\n",
    "        {\"context\": retriever | format_docs, \"query\": RunnablePassthrough()}\n",
    "        | prompt\n",
    "        | (lambda x: print(x.text, end=\"\") or x)  # or x 为了原封不动返回，不影响链条的传递\n",
    "        | llm\n",
    "        | StrOutputParser()  # 输出解析器，将输出解析为字符串\n",
    ")\n",
    "\n",
    "\n",
    "\n",
    "# query = \"不动产或者动产被占有人占有怎么办\"\n",
    "# query = \"杀人犯法吗\"\n",
    "query = \"明天天气怎么样\"\n",
    "response = rag_chains.invoke(query)\n",
    "print(response)\n",
    "\n",
    "\n"
   ],
   "id": "3cb7a559cb4a1774",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "你是一个专业的中文问答助手，擅长基于提供的资料回答用户问题。\n",
      "请仅根据以下背景资料回答问题，如无法找到答案，请直接回答“我不知道”。\n",
      "\n",
      "背景资料：2023 年 是 深入 推 进 实施 “十 四 五 ?战略 规划 、 五 规划 目标 实现 的 关键 之 年 , 抢 抓 机 遇 争 取 承 担 国 推动 * 十 家 重大 科 四 技 任务 ， 管 理 运行 好 重大 观测 设施 ， 组 织 做 好 各 类 科学 数据 处 理 的 应 用 研究 工作 ， 努 力 产 出 更 多 更 大 科学 成 果 。\n",
      "\n",
      "2023 年 工作 总 体 思路 是 : 深入 学 习 贯 彻 党 的 二 十 大 精 神 ， 沙 实 党 的 全 面 领导 ， 落 实 党 中 央 、 国 务 院 和 院 党 组 重 决策 部 署 ， 面 向 国家 重大 需求 、 面 向 国际 天 文科 技 前 沿 、 面 向 经 济 主 战场 ， 加 快 j 全 进 国 家 “十 四 五 ?科技 发 展 规划 实施 ， 加 快 建设 和 持续 推进 国家 重点 实验 室 重 组 工作 ， 加 快 协 同 凝 聚 天 文 领域 科研 队伍 ， 加 强 青年 人 才 队 伍 建设 ， 加 质量 发 展 新 阶段 ， 为 创新 型 国 ATK. 更 REN 家 和 世界 科技 强国 建设 做 出\n",
      "\n",
      "三 、 其 他 事项 说 明\n",
      "\n",
      "问题：明天天气怎么样\n",
      "\n",
      "回答：我不知道\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-14T10:29:41.135497Z",
     "start_time": "2025-10-14T10:29:34.527028Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 带历史对话记录\n",
    "load_dotenv()\n",
    "TONGYI_API_KEY = os.getenv(\"TONGYI_API_KEY\")\n",
    "\n",
    "# 将检索到的文档中的 page_content 取出组合到一起\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n",
    "# Prompt 模板\n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"context\", \"history\", \"query\"],\n",
    "    template=\"\"\"\n",
    "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
    "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
    "\n",
    "背景资料：{context}\n",
    "\n",
    "历史消息：[{history}]\n",
    "\n",
    "问题：{query}\n",
    "\n",
    "回答：\"\"\",\n",
    ")\n",
    "\n",
    "# 大模型\n",
    "llm = Tongyi(model=\"qwen-turbo\", api_key=TONGYI_API_KEY)\n",
    "\n",
    "# 历史消息\n",
    "history = []\n",
    "\n",
    "# 格式化历史消息\n",
    "def format_history(history):\n",
    "    # 只保留最近 3 轮对话记录\n",
    "    max_epoch = 3\n",
    "    if len(history) > 2 * max_epoch:\n",
    "        history = history[-2 * max_epoch :]\n",
    "    return \"\\n\".join([f\"{i['role']}：{i['content']}\" for i in history])\n",
    "\n",
    "# RAG 链条\n",
    "rag_chain = (\n",
    "    {\n",
    "        \"context\": lambda x: format_docs(retriever.invoke(x[\"query\"], k=3)),\n",
    "        \"history\": lambda x: format_history(x[\"history\"]),\n",
    "        \"query\": lambda x: x[\"query\"],\n",
    "    }\n",
    "    | prompt\n",
    "    | (lambda x: print(x.text, end=\"\") or x)\n",
    "    | llm\n",
    "    | StrOutputParser()  # 输出解析器，将输出解析为字符串\n",
    ")\n",
    "\n",
    "query_list = [\"不动产或者动产被人占有怎么办\", \"那要是被损毁了呢\"]\n",
    "for query in query_list:\n",
    "    print(f\"===== 查询: {query} =====\")\n",
    "    response = rag_chain.invoke({\"query\": query, \"history\": history})\n",
    "    print(response, end=\"\\n\\n\")\n",
    "    history.extend(\n",
    "        [\n",
    "            {\"role\": \"用户\", \"content\": query},\n",
    "            {\"role\": \"助手\", \"content\": response},\n",
    "        ]\n",
    "    )\n"
   ],
   "id": "865af397acf1b06a",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "===== 查询: 不动产或者动产被人占有怎么办 =====\n",
      "\n",
      "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
      "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
      "\n",
      "背景资料：第五分编　占有\n",
      "\n",
      "第二十章　占有\n",
      "\n",
      "第四百五十八条　基于合同关系等产生的占有，有关不动产或者动产的使用、收益、违约责任等，按照合同约定；合同没有约定或者约定不明确的，依照有关法律规定。\n",
      "\n",
      "第四百五十九条　占有人因使用占有的不动产或者动产，致使该不动产或者动产受到损害的，恶意占有人应当承担赔偿责任。\n",
      "\n",
      "第四百六十条　不动产或者动产被占有人占有的，权利人可以请求返还原物及其孳息；但是，应当支付善意占有人因维护该不动产或者动产支出的必要费用。\n",
      "\n",
      "第四百六十一条　占有的不动产或者动产毁损、灭失，该不动产或者动产的权利人请求赔偿的，占有人应当将因毁损、灭失取得的保险金、赔偿金或者补偿金等返还给权利人；权利人的损害未得到足够弥补的，恶意占有人还应当赔偿损失。\n",
      "\n",
      "第四百六十二条　占有的不动产或者动产被侵占的，占有人有权请求返还原物；对妨害占有的行为，占有人有权请求排除妨害或者消除危险；因侵占或者妨害造成损害的，占有人有权依法请求损害赔偿。\n",
      "\n",
      "占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。\n",
      "\n",
      "第三编　合同\n",
      "\n",
      "第一分编　通则\n",
      "\n",
      "第一章　一般规定\n",
      "\n",
      "第二百一十八条　权利人、利害关系人可以申请查询、复制不动产登记资料，登记机构应当提供。\n",
      "\n",
      "第二百一十九条　利害关系人不得公开、非法使用权利人的不动产登记资料。\n",
      "\n",
      "第二百二十条　权利人、利害关系人认为不动产登记簿记载的事项错误的，可以申请更正登记。不动产登记簿记载的权利人书面同意更正或者有证据证明登记确有错误的，登记机构应当予以更正。\n",
      "\n",
      "不动产登记簿记载的权利人不同意更正的，利害关系人可以申请异议登记。登记机构予以异议登记，申请人自异议登记之日起十五日内不提起诉讼的，异议登记失效。异议登记不当，造成权利人损害的，权利人可以向申请人请求损害赔偿。\n",
      "\n",
      "第二百二十一条　当事人签订买卖房屋的协议或者签订其他不动产物权的协议，为保障将来实现物权，按照约定可以向登记机构申请预告登记。预告登记后，未经预告登记的权利人同意，处分该不动产的，不发生物权效力。\n",
      "\n",
      "预告登记后，债权消灭或者自能够进行不动产登记之日起九十日内未申请登记的，预告登记失效。\n",
      "\n",
      "第二百二十二条　当事人提供虚假材料申请登记，造成他人损害的，应当承担赔偿责任。\n",
      "\n",
      "第二百二十二条　当事人提供虚假材料申请登记，造成他人损害的，应当承担赔偿责任。\n",
      "\n",
      "因登记错误，造成他人损害的，登记机构应当承担赔偿责任。登记机构赔偿后，可以向造成登记错误的人追偿。\n",
      "\n",
      "第二百二十三条　不动产登记费按件收取，不得按照不动产的面积、体积或者价款的比例收取。\n",
      "\n",
      "第二节　动产交付\n",
      "\n",
      "第二百二十四条　动产物权的设立和转让，自交付时发生效力，但是法律另有规定的除外。\n",
      "\n",
      "第二百二十五条　船舶、航空器和机动车等的物权的设立、变更、转让和消灭，未经登记，不得对抗善意第三人。\n",
      "\n",
      "第二百二十六条　动产物权设立和转让前，权利人已经占有该动产的，物权自民事法律行为生效时发生效力。\n",
      "\n",
      "第二百二十七条　动产物权设立和转让前，第三人占有该动产的，负有交付义务的人可以通过转让请求第三人返还原物的权利代替交付。\n",
      "\n",
      "第二百二十八条　动产物权转让时，当事人又约定由出让人继续占有该动产的，物权自该约定生效时发生效力。\n",
      "\n",
      "第三节　其他规定\n",
      "\n",
      "第二百二十九条　因人民法院、仲裁机构的法律文书或者人民政府的征收决定等，导致物权设立、变更、转让或者消灭的，自法律文书或者征收决定等生效时发生效力。\n",
      "\n",
      "历史消息：[]\n",
      "\n",
      "问题：不动产或者动产被人占有怎么办\n",
      "\n",
      "回答：如果不动产或者动产被他人占有，权利人可以请求返还原物及其孳息，但应当支付善意占有人因维护该不动产或者动产支出的必要费用。如果占有人恶意使用导致不动产或者动产受到损害，应当承担赔偿责任。如果不动产或者动产毁损、灭失，权利人可以请求占有人返还因毁损、灭失取得的保险金、赔偿金或者补偿金等；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。如果不动产或者动产被侵占，占有人有权请求返还原物，对妨害占有的行为，占有人有权请求排除妨害或者消除危险；因侵占或者妨害造成损害的，占有人有权依法请求损害赔偿。占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。\n",
      "\n",
      "===== 查询: 那要是被损毁了呢 =====\n",
      "\n",
      "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
      "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
      "\n",
      "背景资料：（五）恢复原状；\n",
      "\n",
      "（六）修理、重作、更换；\n",
      "\n",
      "（七）继续履行；\n",
      "\n",
      "（八）赔偿损失；\n",
      "\n",
      "（九）支付违约金；\n",
      "\n",
      "（十）消除影响、恢复名誉；\n",
      "\n",
      "（十一）赔礼道歉。\n",
      "\n",
      "法律规定惩罚性赔偿的，依照其规定。\n",
      "\n",
      "本条规定的承担民事责任的方式，可以单独适用，也可以合并适用。\n",
      "\n",
      "第一百八十条　因不可抗力不能履行民事义务的，不承担民事责任。法律另有规定的，依照其规定。\n",
      "\n",
      "不可抗力是不能预见、不能避免且不能克服的客观情况。\n",
      "\n",
      "第一百八十一条　因正当防卫造成损害的，不承担民事责任。\n",
      "\n",
      "正当防卫超过必要的限度，造成不应有的损害的，正当防卫人应当承担适当的民事责任。\n",
      "\n",
      "第一百八十二条　因紧急避险造成损害的，由引起险情发生的人承担民事责任。\n",
      "\n",
      "危险由自然原因引起的，紧急避险人不承担民事责任，可以给予适当补偿。\n",
      "\n",
      "紧急避险采取措施不当或者超过必要的限度，造成不应有的损害的，紧急避险人应当承担适当的民事责任。\n",
      "\n",
      "第一百八十三条　因保护他人民事权益使自己受到损害的，由侵权人承担民事责任，受益人可以给予适当补偿。没有侵权人、侵权人逃逸或者无力承担民事责任，受害人请求补偿的，受益人应当给予适当补偿。\n",
      "\n",
      "第一千一百七十五条　损害是因第三人造成的，第三人应当承担侵权责任。\n",
      "\n",
      "第一千一百七十六条　自愿参加具有一定风险的文体活动，因其他参加者的行为受到损害的，受害人不得请求其他参加者承担侵权责任；但是，其他参加者对损害的发生有故意或者重大过失的除外。\n",
      "\n",
      "活动组织者的责任适用本法第一千一百九十八条至第一千二百零一条的规定。\n",
      "\n",
      "第一千一百七十七条　合法权益受到侵害，情况紧迫且不能及时获得国家机关保护，不立即采取措施将使其合法权益受到难以弥补的损害的，受害人可以在保护自己合法权益的必要范围内采取扣留侵权人的财物等合理措施；但是，应当立即请求有关国家机关处理。\n",
      "\n",
      "受害人采取的措施不当造成他人损害的，应当承担侵权责任。\n",
      "\n",
      "第一千一百七十八条　本法和其他法律对不承担责任或者减轻责任的情形另有规定的，依照其规定。\n",
      "\n",
      "第二章　损害赔偿\n",
      "\n",
      "第一千一百七十九条　侵害他人造成人身损害的，应当赔偿医疗费、护理费、交通费、营养费、住院伙食补助费等为治疗和康复支出的合理费用，以及因误工减少的收入。造成残疾的，还应当赔偿辅助器具费和残疾赔偿金；造成死亡的，还应当赔偿丧葬费和死亡赔偿金。\n",
      "\n",
      "第一千二百五十三条　建筑物、构筑物或者其他设施及其搁置物、悬挂物发生脱落、坠落造成他人损害，所有人、管理人或者使用人不能证明自己没有过错的，应当承担侵权责任。所有人、管理人或者使用人赔偿后，有其他责任人的，有权向其他责任人追偿。\n",
      "\n",
      "第一千二百五十四条　禁止从建筑物中抛掷物品。从建筑物中抛掷物品或者从建筑物上坠落的物品造成他人损害的，由侵权人依法承担侵权责任；经调查难以确定具体侵权人的，除能够证明自己不是侵权人的外，由可能加害的建筑物使用人给予补偿。可能加害的建筑物使用人补偿后，有权向侵权人追偿。\n",
      "\n",
      "物业服务企业等建筑物管理人应当采取必要的安全保障措施防止前款规定情形的发生；未采取必要的安全保障措施的，应当依法承担未履行安全保障义务的侵权责任。\n",
      "\n",
      "发生本条第一款规定的情形的，公安等机关应当依法及时调查，查清责任人。\n",
      "\n",
      "第一千二百五十五条　堆放物倒塌、滚落或者滑落造成他人损害，堆放人不能证明自己没有过错的，应当承担侵权责任。\n",
      "\n",
      "历史消息：[用户：不动产或者动产被人占有怎么办\n",
      "助手：如果不动产或者动产被他人占有，权利人可以请求返还原物及其孳息，但应当支付善意占有人因维护该不动产或者动产支出的必要费用。如果占有人恶意使用导致不动产或者动产受到损害，应当承担赔偿责任。如果不动产或者动产毁损、灭失，权利人可以请求占有人返还因毁损、灭失取得的保险金、赔偿金或者补偿金等；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。如果不动产或者动产被侵占，占有人有权请求返还原物，对妨害占有的行为，占有人有权请求排除妨害或者消除危险；因侵占或者妨害造成损害的，占有人有权依法请求损害赔偿。占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。]\n",
      "\n",
      "问题：那要是被损毁了呢\n",
      "\n",
      "回答：如果不动产或者动产被损毁，权利人可以请求占有人返还因毁损、灭失取得的保险金、赔偿金或者补偿金等；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。\n",
      "\n"
     ]
    }
   ],
   "execution_count": 5
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-14T10:34:30.702042Z",
     "start_time": "2025-10-14T10:34:15.787713Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 重述用户消息\n",
    "load_dotenv()\n",
    "TONGYI_API_KEY = os.getenv(\"TONGYI_API_KEY\")\n",
    "\n",
    "# 将检索到的文档中的 page_content 取出组合到一起\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n",
    "# Prompt 模板\n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"context\", \"history\", \"query\"],\n",
    "    template=\"\"\"\n",
    "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
    "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
    "\n",
    "背景资料：{context}\n",
    "\n",
    "历史消息：[{history}]\n",
    "\n",
    "问题：{query}\n",
    "\n",
    "回答：\"\"\",\n",
    ")\n",
    "\n",
    "# 大模型\n",
    "llm = Tongyi(model=\"qwen-turbo\", api_key=TONGYI_API_KEY)\n",
    "\n",
    "# 历史消息\n",
    "history = []\n",
    "\n",
    "# 格式化历史消息\n",
    "def format_history(history):\n",
    "    # 只保留最近 3 轮对话记录\n",
    "    max_epoch = 3\n",
    "    if len(history) > 2 * max_epoch:\n",
    "        history = history[-2 * max_epoch :]\n",
    "    return \"\\n\".join([f\"{i['role']}：{i['content']}\" for i in history])\n",
    "\n",
    "# ！！！rephrase Prompt 模板\n",
    "rephrase_prompt = PromptTemplate(\n",
    "    input_variables=[\"history\", \"query\"],\n",
    "    template=\"\"\"\n",
    "根据历史消息简要完善用户的问题，使其更加具体。只输出完善后的问题。\n",
    "\n",
    "历史消息：[{history}]\n",
    "\n",
    "问题：{query}\n",
    "\"\"\",\n",
    ")\n",
    "\n",
    "# ！！！重述链条：根据历史和当前 query 生成更具体问题\n",
    "rephrase_chain = (\n",
    "    {\n",
    "        \"history\": lambda x: format_history(x[\"history\"]),\n",
    "        \"query\": lambda x: x[\"query\"],\n",
    "    }\n",
    "    | rephrase_prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    "    | (lambda x: print(f\"===== 重述后的查询: {x}=====\") or x)\n",
    ")\n",
    "\n",
    "# Prompt 模板\n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"context\", \"history\", \"query\"],\n",
    "    template=\"\"\"\n",
    "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
    "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
    "\n",
    "背景资料：{context}\n",
    "\n",
    "历史消息：[{history}]\n",
    "\n",
    "问题：{query}\n",
    "\n",
    "回答：\"\"\",\n",
    ")\n",
    "\n",
    "# ！！！RAG 链条:  使用重述后的 query进行检索\n",
    "rag_chain = (\n",
    "    {\n",
    "        \"context\": lambda x: format_docs(\n",
    "            retriever.invoke(\n",
    "                rephrase_chain.invoke({\"history\": x.get(\"history\"), \"query\": x.get(\"query\")}),\n",
    "                k=3,\n",
    "            )\n",
    "        ),\n",
    "        \"history\": lambda x: format_history(x.get(\"history\")),\n",
    "        \"query\": lambda x: x.get(\"query\"),\n",
    "    }\n",
    "    | prompt\n",
    "    | (lambda x: print(x.text, end=\"\") or x)\n",
    "    | llm\n",
    "    | StrOutputParser()  # 输出解析器，将输出解析为字符串\n",
    ")\n",
    "\n",
    "query_list = [\"不动产或者动产被人占有怎么办\", \"那要是被损毁了呢\"]\n",
    "for query in query_list:\n",
    "    print(f\"===== 查询: {query} =====\")\n",
    "    response = rag_chain.invoke({\"query\": query, \"history\": history})\n",
    "    print(response, end=\"\\n\\n\")\n",
    "    history.extend(\n",
    "        [\n",
    "            {\"role\": \"用户\", \"content\": query},\n",
    "            {\"role\": \"助手\", \"content\": response},\n",
    "        ]\n",
    "    )\n"
   ],
   "id": "35a167d751568497",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "===== 查询: 不动产或者动产被人占有怎么办 =====\n",
      "===== 重述后的查询: 不动产或者动产被人占有怎么办？如何依法维护自己的合法权益？=====\n",
      "\n",
      "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
      "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
      "\n",
      "背景资料：第五分编　占有\n",
      "\n",
      "第二十章　占有\n",
      "\n",
      "第四百五十八条　基于合同关系等产生的占有，有关不动产或者动产的使用、收益、违约责任等，按照合同约定；合同没有约定或者约定不明确的，依照有关法律规定。\n",
      "\n",
      "第四百五十九条　占有人因使用占有的不动产或者动产，致使该不动产或者动产受到损害的，恶意占有人应当承担赔偿责任。\n",
      "\n",
      "第四百六十条　不动产或者动产被占有人占有的，权利人可以请求返还原物及其孳息；但是，应当支付善意占有人因维护该不动产或者动产支出的必要费用。\n",
      "\n",
      "第四百六十一条　占有的不动产或者动产毁损、灭失，该不动产或者动产的权利人请求赔偿的，占有人应当将因毁损、灭失取得的保险金、赔偿金或者补偿金等返还给权利人；权利人的损害未得到足够弥补的，恶意占有人还应当赔偿损失。\n",
      "\n",
      "第四百六十二条　占有的不动产或者动产被侵占的，占有人有权请求返还原物；对妨害占有的行为，占有人有权请求排除妨害或者消除危险；因侵占或者妨害造成损害的，占有人有权依法请求损害赔偿。\n",
      "\n",
      "占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。\n",
      "\n",
      "第三编　合同\n",
      "\n",
      "第一分编　通则\n",
      "\n",
      "第一章　一般规定\n",
      "\n",
      "国家实行社会主义市场经济，保障一切市场主体的平等法律地位和发展权利。\n",
      "\n",
      "第二百零七条　国家、集体、私人的物权和其他权利人的物权受法律平等保护，任何组织或者个人不得侵犯。\n",
      "\n",
      "第二百零八条　不动产物权的设立、变更、转让和消灭，应当依照法律规定登记。动产物权的设立和转让，应当依照法律规定交付。\n",
      "\n",
      "第二章　物权的设立、变更、转让和消灭\n",
      "\n",
      "第一节　不动产登记\n",
      "\n",
      "第二百零九条　不动产物权的设立、变更、转让和消灭，经依法登记，发生效力；未经登记，不发生效力，但是法律另有规定的除外。\n",
      "\n",
      "依法属于国家所有的自然资源，所有权可以不登记。\n",
      "\n",
      "第二百一十条　不动产登记，由不动产所在地的登记机构办理。\n",
      "\n",
      "国家对不动产实行统一登记制度。统一登记的范围、登记机构和登记办法，由法律、行政法规规定。\n",
      "\n",
      "第二百一十一条　当事人申请登记，应当根据不同登记事项提供权属证明和不动产界址、面积等必要材料。\n",
      "\n",
      "第二百一十二条　登记机构应当履行下列职责：\n",
      "\n",
      "（一）查验申请人提供的权属证明和其他必要材料；\n",
      "\n",
      "（二）就有关登记事项询问申请人；\n",
      "\n",
      "（三）如实、及时登记有关事项；\n",
      "\n",
      "（四）法律、行政法规规定的其他职责。\n",
      "\n",
      "第二百一十八条　权利人、利害关系人可以申请查询、复制不动产登记资料，登记机构应当提供。\n",
      "\n",
      "第二百一十九条　利害关系人不得公开、非法使用权利人的不动产登记资料。\n",
      "\n",
      "第二百二十条　权利人、利害关系人认为不动产登记簿记载的事项错误的，可以申请更正登记。不动产登记簿记载的权利人书面同意更正或者有证据证明登记确有错误的，登记机构应当予以更正。\n",
      "\n",
      "不动产登记簿记载的权利人不同意更正的，利害关系人可以申请异议登记。登记机构予以异议登记，申请人自异议登记之日起十五日内不提起诉讼的，异议登记失效。异议登记不当，造成权利人损害的，权利人可以向申请人请求损害赔偿。\n",
      "\n",
      "第二百二十一条　当事人签订买卖房屋的协议或者签订其他不动产物权的协议，为保障将来实现物权，按照约定可以向登记机构申请预告登记。预告登记后，未经预告登记的权利人同意，处分该不动产的，不发生物权效力。\n",
      "\n",
      "预告登记后，债权消灭或者自能够进行不动产登记之日起九十日内未申请登记的，预告登记失效。\n",
      "\n",
      "第二百二十二条　当事人提供虚假材料申请登记，造成他人损害的，应当承担赔偿责任。\n",
      "\n",
      "历史消息：[]\n",
      "\n",
      "问题：不动产或者动产被人占有怎么办\n",
      "\n",
      "回答：如果不动产或者动产被他人占有，权利人可以请求返还原物及其孳息，但是应当支付善意占有人因维护该不动产或者动产支出的必要费用。如果占有人是恶意的，因其使用占有的不动产或者动产致使该不动产或者动产受到损害的，应当承担赔偿责任。如果不动产或者动产毁损、灭失，权利人可以请求占有人将因毁损、灭失取得的保险金、赔偿金或者补偿金等返还给权利人；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。此外，如果占有的不动产或者动产被侵占，占有人有权请求返还原物，对妨害占有的行为，有权请求排除妨害或者消除危险，因侵占或者妨害造成损害的，有权依法请求损害赔偿。占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。\n",
      "\n",
      "===== 查询: 那要是被损毁了呢 =====\n",
      "===== 重述后的查询: 那如果是不动产或者动产被损毁了，权利人可以请求哪些赔偿？=====\n",
      "\n",
      "你是一个专业的中文问答助手，擅长基于提供的资料回答问题。\n",
      "请仅根据以下背景资料以及历史消息回答问题，如无法找到答案，请直接回答“我不知道”。\n",
      "\n",
      "背景资料：第二百一十八条　权利人、利害关系人可以申请查询、复制不动产登记资料，登记机构应当提供。\n",
      "\n",
      "第二百一十九条　利害关系人不得公开、非法使用权利人的不动产登记资料。\n",
      "\n",
      "第二百二十条　权利人、利害关系人认为不动产登记簿记载的事项错误的，可以申请更正登记。不动产登记簿记载的权利人书面同意更正或者有证据证明登记确有错误的，登记机构应当予以更正。\n",
      "\n",
      "不动产登记簿记载的权利人不同意更正的，利害关系人可以申请异议登记。登记机构予以异议登记，申请人自异议登记之日起十五日内不提起诉讼的，异议登记失效。异议登记不当，造成权利人损害的，权利人可以向申请人请求损害赔偿。\n",
      "\n",
      "第二百二十一条　当事人签订买卖房屋的协议或者签订其他不动产物权的协议，为保障将来实现物权，按照约定可以向登记机构申请预告登记。预告登记后，未经预告登记的权利人同意，处分该不动产的，不发生物权效力。\n",
      "\n",
      "预告登记后，债权消灭或者自能够进行不动产登记之日起九十日内未申请登记的，预告登记失效。\n",
      "\n",
      "第二百二十二条　当事人提供虚假材料申请登记，造成他人损害的，应当承担赔偿责任。\n",
      "\n",
      "第二百二十二条　当事人提供虚假材料申请登记，造成他人损害的，应当承担赔偿责任。\n",
      "\n",
      "因登记错误，造成他人损害的，登记机构应当承担赔偿责任。登记机构赔偿后，可以向造成登记错误的人追偿。\n",
      "\n",
      "第二百二十三条　不动产登记费按件收取，不得按照不动产的面积、体积或者价款的比例收取。\n",
      "\n",
      "第二节　动产交付\n",
      "\n",
      "第二百二十四条　动产物权的设立和转让，自交付时发生效力，但是法律另有规定的除外。\n",
      "\n",
      "第二百二十五条　船舶、航空器和机动车等的物权的设立、变更、转让和消灭，未经登记，不得对抗善意第三人。\n",
      "\n",
      "第二百二十六条　动产物权设立和转让前，权利人已经占有该动产的，物权自民事法律行为生效时发生效力。\n",
      "\n",
      "第二百二十七条　动产物权设立和转让前，第三人占有该动产的，负有交付义务的人可以通过转让请求第三人返还原物的权利代替交付。\n",
      "\n",
      "第二百二十八条　动产物权转让时，当事人又约定由出让人继续占有该动产的，物权自该约定生效时发生效力。\n",
      "\n",
      "第三节　其他规定\n",
      "\n",
      "第二百二十九条　因人民法院、仲裁机构的法律文书或者人民政府的征收决定等，导致物权设立、变更、转让或者消灭的，自法律文书或者征收决定等生效时发生效力。\n",
      "\n",
      "第一千一百八十条　因同一侵权行为造成多人死亡的，可以以相同数额确定死亡赔偿金。\n",
      "\n",
      "第一千一百八十一条　被侵权人死亡的，其近亲属有权请求侵权人承担侵权责任。被侵权人为组织，该组织分立、合并的，承继权利的组织有权请求侵权人承担侵权责任。\n",
      "\n",
      "被侵权人死亡的，支付被侵权人医疗费、丧葬费等合理费用的人有权请求侵权人赔偿费用，但是侵权人已经支付该费用的除外。\n",
      "\n",
      "第一千一百八十二条　侵害他人人身权益造成财产损失的，按照被侵权人因此受到的损失或者侵权人因此获得的利益赔偿；被侵权人因此受到的损失以及侵权人因此获得的利益难以确定，被侵权人和侵权人就赔偿数额协商不一致，向人民法院提起诉讼的，由人民法院根据实际情况确定赔偿数额。\n",
      "\n",
      "第一千一百八十三条　侵害自然人人身权益造成严重精神损害的，被侵权人有权请求精神损害赔偿。\n",
      "\n",
      "因故意或者重大过失侵害自然人具有人身意义的特定物造成严重精神损害的，被侵权人有权请求精神损害赔偿。\n",
      "\n",
      "第一千一百八十四条　侵害他人财产的，财产损失按照损失发生时的市场价格或者其他合理方式计算。\n",
      "\n",
      "第一千一百八十五条　故意侵害他人知识产权，情节严重的，被侵权人有权请求相应的惩罚性赔偿。\n",
      "\n",
      "历史消息：[用户：不动产或者动产被人占有怎么办\n",
      "助手：如果不动产或者动产被他人占有，权利人可以请求返还原物及其孳息，但是应当支付善意占有人因维护该不动产或者动产支出的必要费用。如果占有人是恶意的，因其使用占有的不动产或者动产致使该不动产或者动产受到损害的，应当承担赔偿责任。如果不动产或者动产毁损、灭失，权利人可以请求占有人将因毁损、灭失取得的保险金、赔偿金或者补偿金等返还给权利人；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。此外，如果占有的不动产或者动产被侵占，占有人有权请求返还原物，对妨害占有的行为，有权请求排除妨害或者消除危险，因侵占或者妨害造成损害的，有权依法请求损害赔偿。占有人返还原物的请求权，自侵占发生之日起一年内未行使的，该请求权消灭。]\n",
      "\n",
      "问题：那要是被损毁了呢\n",
      "\n",
      "回答：如果不动产或者动产被损毁，权利人可以请求占有人将因毁损、灭失取得的保险金、赔偿金或者补偿金等返还给权利人；如果权利人的损害未得到足够弥补，恶意占有人还应当赔偿损失。此外，对于妨害占有的行为，占有人有权请求排除妨害或者消除危险，因侵占或者妨害造成损害的，有权依法请求损害赔偿。\n",
      "\n"
     ]
    }
   ],
   "execution_count": 6
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
